/**
 * dom操作模块, 更多dom操作可以使用 element-ui/lib/util/dom
 * @module utils/dom
 */

/**
 * 获取元素 offsetLeft
 * @deprecated el.getBoundingClientRect().left
 * @param {HTMLElement} el DOM元素
 * @returns {Number}
 */
export function offsetLeft(el: HTMLElement) {
	return el.getBoundingClientRect().left;
}

/**
 * 获取元素 offsetTop
 * @deprecated el.getBoundingClientRect().top
 * @param {HTMLElement} el DOM元素
 * @returns {Number}
 */
export function offsetTop(el: HTMLElement) {
	return el.getBoundingClientRect().top;
}

/**
 * 获取的窗体的滚动距离
 * @param {Window|HTMLElement} target window 或 DOM元素
 * @param {Boolean} top 是否垂直方向
 * @returns {Number} 滚动距离
 */
export function getScroll(target: Window | HTMLElement, top: boolean = false) {
	if (!target) return 0;
	if (target === window) {
		const prop = top ? 'pageYOffset' : 'pageXOffset';
		const method = top ? 'scrollTop' : 'scrollLeft';

		let ret = target[prop];

		if (typeof ret !== 'number') {
			ret = window.document.documentElement[method];
		}

		return ret;
	} else {
		const method = top ? 'scrollTop' : 'scrollLeft';
		return (target as HTMLElement)[method];
	}
}

/**
 * 获取元素的 offset
 * @deprecated  el.getBoundingClientRect()
 * @param {HTMLElement} element DOM元素
 * @returns {Object} offset {left, top}
 */
export function getOffset(element: HTMLElement) {
	const rect = element.getBoundingClientRect();

	const scrollTop = getScroll(window, true);
	const scrollLeft = getScroll(window);

	const docEl = window.document.body;
	const clientTop = docEl.clientTop || 0;
	const clientLeft = docEl.clientLeft || 0;

	return {
		top: rect.top + scrollTop - clientTop,
		left: rect.left + scrollLeft - clientLeft
	};
}

/**
 * 动画滚动到顶部 scrollTop animation
 * @param {HTMLElement} el DOM元素
 * @param {Number} [from=0] 开始位置
 * @param {Number} to  滚动到位置
 * @param {Number} [duration=500] 动画持续时间
 */
export function scrollTop(el: Window | HTMLElement, from = 0, to: number, duration = 500) {
	if (!window.requestAnimationFrame) {
		window.requestAnimationFrame = function (callback) {
			return window.setTimeout(callback, 1000 / 60);
		};
	}
	const difference = Math.abs(from - to);
	const step = Math.ceil((difference / duration) * 50);

	function scroll(start: number, end: number, step: number) {
		if (start === end) return;

		let d = start + step > end ? end : start + step;
		if (start > end) {
			d = start - step < end ? end : start - step;
		}

		if (el === window) {
			window.scrollTo(d, d);
		} else {
			(el as HTMLElement).scrollTop = d;
		}
		window.requestAnimationFrame(() => scroll(d, end, step));
	}

	scroll(from, to, step);
}

/**
 * 滚动到顶部
 * @param {HTMLElement} el DOM元素
 * 将元素滚动到顶部最简单的方法是使用scrollIntoView。设置block为start可以滚动到顶部；设置behavior为smooth可以开启平滑滚动。
 */
export function scrollToTop(el: HTMLElement) {
	el.scrollIntoView({ behavior: 'smooth', block: 'start' });
}

/**
 * 滚动到底部
 * @param {HTMLElement} el DOM元素
 * 与滚动到顶部一样，滚动到底部只需要设置block为end即可。
 */
export function scrollToBottom(el: HTMLElement) {
	el.scrollIntoView({ behavior: 'smooth', block: 'end' });
}

/**
 * 在元素前插入元素
 * @param {HTMLElement} newEl 新DOM元素
 * @param {HTMLElement} targetEl 在指定目标DOM元素前插入
 */
export function insertAfter(newEl: HTMLElement, targetEl: HTMLElement) {
	const parentEl = targetEl.parentNode;
	if (parentEl?.lastChild === targetEl) {
		parentEl.appendChild(newEl);
	} else {
		parentEl?.insertBefore(newEl, targetEl.nextSibling);
	}
}

/**
 * 判断元素是否隐藏
 * @param  {HTMLElement} el 元素DOM对象
 * @return {boolean} 是否隐藏
 */
export function isHidden(el: HTMLElement) {
	const style = window.getComputedStyle(el);
	return style.display === 'none' || style.visibility === 'hidden';
}

/**
 * dom添加监听事件
 * @param {HTMLElement} element DOM元素
 * @param {string} event	事件类型
 * @param {function} handler	事件处理函数
 * @param {boolean} useCapture	是否使用捕获模式
 */
export function on(element: HTMLElement, event: string, handler: (...args: any[]) => void, useCapture = false) {
	if (element && event && handler) {
		element == null ? void 0 : element.addEventListener(event, handler, useCapture);
	}
}

/**
 * dom取消监听事件
 * @param {HTMLElement} element DOM元素
 * @param {string} event	事件类型
 * @param {function} handler	事件处理函数
 * @param {boolean} useCapture	是否使用捕获模式
 */
export function off(element: HTMLElement, event: string, handler: (...args: any[]) => void, useCapture = false) {
	if (element && event && handler) {
		element == null ? void 0 : element.removeEventListener(event, handler, useCapture);
	}
}
